Day 6 我們優化了系統性能。但優化得好不好,怎麼衡量?「感覺變快了」不算。今天我們建設完整的評測框架:20 個標準 SRS 樣本、自動計算 F1 分數、對比不同版本性能。
完成本篇後,你將擁有:
具體目標: 實現 src/evaluator.py,能輸出「Precision: 0.85, Recall: 0.78, F1: 0.81」這樣的量化結果。
前六天我們造了一個完整的系統,能讀、能分、能檢測、能並行、還有快取。看起來不錯。
但你怎麼知道它是否真的在檢測衝突?
執行一次,說「檢測到 8 個衝突」。然後呢?是真的有 8 個,還是漏掉了 20 個,誤報了 5 個?無法驗證。
「這次改進了代碼,性能提升了」——提升了多少?比上次好還是更差?無法對比。
你需要客觀的指標。
簡單的思路:
這就是「評測框架」。
Precision(精確度)
簡單說:「檢測到的衝突中,有多少比例是真的?」
公式:Precision = 真檢測數 / (真檢測數 + 誤報數)
例子:檢測到 10 個衝突,其中 8 個實際存在,2 個是誤報。Precision = 8/10 = 80%。
含義:精確度高 = 誤報少。你信任系統說「有矛盾」。
Recall(召回率)
簡單說:「實際有的衝突中,檢測到了多少?」
公式:Recall = 真檢測數 / (真檢測數 + 漏報數)
例子:實際 10 個衝突,檢測到 8 個。Recall = 8/10 = 80%。
含義:召回率高 = 漏報少。系統能找全。
F1 分數
Precision 和 Recall 的調和平均。當你同時在乎誤報和漏報時用。
公式:F1 = 2 × (Precision × Recall) / (Precision + Recall)
使用場景:
src/evaluation.py:
from dataclasses import dataclass
from typing import List, Set, Tuple
from enum import Enum
class ConflictType(str, Enum):
LOGIC = "邏輯矛盾"
SECURITY = "安全性衝突"
PERFORMANCE = "性能衝突"
@dataclass
class GroundTruthConflict:
"""預期的衝突(標準答案)"""
req_id_1: str
req_id_2: str
conflict_type: ConflictType
description: str
@dataclass
class DetectedConflict:
"""檢測到的衝突"""
req_id_1: str
req_id_2: str
conflict_type: str
severity: str
class EvaluationMetrics:
"""評測指標計算"""
def __init__(self):
self.tp = 0 # 真正例(應檢測且檢測到)
self.fp = 0 # 假正例(不應檢測但檢測到)
self.fn = 0 # 假負例(應檢測但未檢測到)
@property
def precision(self) -> float:
if self.tp + self.fp == 0:
return 0.0
return self.tp / (self.tp + self.fp)
@property
def recall(self) -> float:
if self.tp + self.fn == 0:
return 0.0
return self.tp / (self.tp + self.fn)
@property
def f1_score(self) -> float:
p, r = self.precision, self.recall
if p + r == 0:
return 0.0
return 2 * (p * r) / (p + r)
class Evaluator:
"""評估器:比較預期與檢測結果"""
def __init__(self, ground_truth: List[GroundTruthConflict]):
# 標準化格式:(小ID, 大ID) 方便比較
self.truth_set: Set[Tuple[str, str]] = {
self._normalize(c.req_id_1, c.req_id_2)
for c in ground_truth
}
@staticmethod
def _normalize(id1: str, id2: str) -> Tuple[str, str]:
return (id1, id2) if id1 < id2 else (id2, id1)
def evaluate(self, detected: List[DetectedConflict]) -> EvaluationMetrics:
metrics = EvaluationMetrics()
detected_set = {
self._normalize(c.req_id_1, c.req_id_2)
for c in detected
}
# TP:檢測到且實際存在
for pair in detected_set:
if pair in self.truth_set:
metrics.tp += 1
else:
metrics.fp += 1
# FN:應該檢測但沒檢測到
for pair in self.truth_set:
if pair not in detected_set:
metrics.fn += 1
return metrics
準備 3 個域的完整測試集:
電商平台
醫療系統
社交媒體
# 1. 定義標準答案
ground_truth = [
GroundTruthConflict("REQ-2.1.1", "REQ-8.4.2", ConflictType.LOGIC, "..."),
# ... 更多
]
# 2. 運行檢測器
detected = your_detector.detect_conflicts(srs_content)
# 3. 評測
evaluator = Evaluator(ground_truth)
metrics = evaluator.evaluate(detected)
# 4. 查看結果
print(f"Precision: {metrics.precision:.3f}")
print(f"Recall: {metrics.recall:.3f}")
print(f"F1: {metrics.f1_score:.3f}")
假設測試電商平台,預期 4 個衝突:
情況 1:完美檢測
情況 2:漏報
情況 3:誤報
情況 4:過檢測
哪個最好?看場景。如果你不能容忍漏報(安全審計),情況 4 是好的(Recall 完美)。如果你不能容忍誤報(成本高),情況 2 是好的(Precision 完美)。
在 /Users/imac-4096/Desktop/srs-review-agent 目錄中執行:
# 執行評測
python3 -m pytest tests/test_day7_evaluation.py -v
# 或運行評測指令碼
python3 tests/test_day7_evaluation.py
🧪 Day 7 評測系統單元測試
======================================================================
測試 1:Precision 計算
✅ 正確:TP=8, FP=2, Precision=0.8
測試 2:Recall 計算
✅ 正確:TP=8, FN=2, Recall=0.8
測試 3:F1 分數計算
✅ 正確:Precision=0.8, Recall=0.8, F1=0.8
======================================================================
評測結果摘要
======================================================================
20 個測試用例評測:
• 平均 Precision:0.78
• 平均 Recall:0.81
• 平均 F1 分數:0.789
基線性能:✅ F1 = 0.789
======================================================================
✅ 評測框架正常初始化
✅ Precision/Recall/F1 計算正確
✅ 20 個測試用例通過
✅ 基線 F1 分數 ≥ 0.75
✅ 評測報告可輸出
python3 tests/test_day7_evaluation.py
會輸出每個測試用例的 Precision、Recall、F1,然後是平均分。
用這個作為基準線。每次改進代碼後重新評測,看指標有沒有提升。
可量化:不再「感覺變好了」,而是「F1 從 0.75 升到 0.82」。
可對比:不同版本之間有客觀的分數。
可追蹤:知道系統在哪裡失敗。Recall 低?漏報多,需要添加新檢測規則。Precision 低?誤報多,需要精化規則。
可指導改進:分析失敗案例,了解系統缺陷,有針對性地改進。
Day 8 我們會根據評測結果,分析「為什麼漏報了這些」「為什麼誤報了那些」,然後優化檢測規則。
現在你有了衡量標尺。接下來就是打磨系統讓指標一步步提升。
第 1 週完成 ✅
第 2 週:優化與驗證
🎉 現在我們可以用數據說話了